1 module codegen_d; 2 import defs; 3 import util; 4 5 void gencode_d(OGLFunctionFamily[] functionFamilies, OGLEnumGroup[] enums, string filename, string modulename, bool isStatic, string containerStruct) { 6 import std.conv : text; 7 8 char[] ret; 9 10 // For OpenGL 4.5 output from the "printers" is around 1mb, 11 // so 6mb is beyond anything we'll actually need. 12 // But it'll increase speed enough that it is worth 13 // the actual work to get it in code. 14 ret.reserve(1024 * 1024 * 6); // 1kb * 1mb * 6mb 15 16 if (isStatic) { 17 // seriously what do you expect me to do with this? 18 containerStruct = null; 19 } 20 21 string prefixContainer, prefix, prefixComment; 22 if (containerStruct !is null) { 23 if (isStatic) { 24 prefixContainer = " "; 25 prefix = " "; 26 } else { 27 prefixContainer = ""; 28 prefix = " "; 29 } 30 } else { 31 prefixContainer = ""; 32 prefix = " "; 33 } 34 35 prefixComment = prefix ~ " + "; 36 37 if (modulename !is null) { 38 ret ~= " 39 /** 40 * This is a set of OpenGL bindings. 41 * 42 * Generated by ./ogl_gen .... 43 * Do not modify. Regenerate if changes are required. 44 * 45 * Macros: 46 * D_CODE = <pre><code class=\"D\">$0</code></pre> 47 */\n"[1 .. $]; 48 49 ret ~= "module "; 50 ret ~= modulename; 51 ret ~= ";\n"; 52 } else { 53 ret ~= " 54 /** 55 * Macros: 56 * D_CODE = <pre><code class=\"D\">$0</code></pre> 57 */\n"[1 .. $]; 58 } 59 60 ret ~= import("D.d").removeUnicodeBOM; 61 ret ~= "\n"; 62 63 import std.algorithm; 64 import std.range : array; 65 66 foreach(e; enums 67 .map!(a => a.enums) 68 .joiner 69 .array 70 .sort!((a, b) => a.name < b.name) 71 .uniq!((a, b) => a.name == b.name)) { 72 if (e.value !is null) { 73 ret ~= "enum "; 74 ret ~= e.name; 75 ret ~= " = "; 76 77 if (e.value.length > 2 && e.value[0 .. 2] != "0x") 78 ret ~= "0x"; 79 ret ~= e.value; 80 ret ~= "; ///\n"; 81 } 82 } 83 ret ~= "\n"; 84 85 if (containerStruct !is null) { 86 ret ~= "/// Container for bindings\n"; 87 ret ~= "struct "; 88 ret ~= containerStruct; 89 ret ~= " {\n"; 90 } 91 92 foreach(grp; enums) { 93 if (grp.name.length > 0) { 94 bool haveAnyValid; 95 foreach(e; grp.enums) { 96 if (e.value !is null) { 97 haveAnyValid = true; 98 } 99 } 100 if (!haveAnyValid) 101 continue; 102 103 ret ~= prefixContainer; 104 ret ~= "\t///\n\t"; 105 106 if (grp.isBitmask) { 107 ret ~= prefixContainer; 108 ret ~= "@Bitmaskable\n\t"; 109 } 110 111 ret ~= prefixContainer; 112 ret ~= "enum "; 113 ret ~= grp.name; 114 ret ~= " {\n"; 115 116 size_t i; 117 foreach(e; grp.enums) { 118 if (e.value !is null) { 119 if (i > 0) 120 ret ~= ",\n"; 121 122 ret ~= prefix; 123 ret ~= "\t///\n\t"; 124 125 ret ~= prefix; 126 if (e.name.length > 3 && e.name[0 .. 3] == "GL_") { 127 ret ~= e.name[3 .. $].makeValidCIdentifier; 128 } else { 129 ret ~= e.name; 130 } 131 132 ret ~= " = "; 133 if (e.value.length > 2 && e.value[0 .. 2] != "0x") 134 ret ~= "0x"; 135 136 ret ~= e.value; 137 i++; 138 } 139 } 140 141 ret ~= "\n\t"; 142 ret ~= prefixContainer; 143 ret ~= "}\n\n"; 144 } 145 } 146 147 if (isStatic) { 148 ret ~= prefixContainer; 149 ret ~= "extern(System) {\n"; 150 } 151 152 foreach(k, family; functionFamilies) { 153 foreach(i, func; family.functions) { 154 string argsSignature; 155 if (func.argNames.length == 1 && func.argTypes[0] == "void") { 156 } else { 157 foreach(j, arg; func.argNames) { 158 if (j > 0) 159 argsSignature ~= ", "; 160 161 argsSignature ~= func.argTypes[j]; 162 if (arg !is null) { 163 argsSignature ~= " "; 164 165 switch(arg) { 166 case "ref": 167 argsSignature ~= "ref_"; 168 break; 169 case "in": 170 argsSignature ~= "in_"; 171 break; 172 case "out": 173 argsSignature ~= "out_"; 174 break; 175 default: 176 argsSignature ~= arg; 177 } 178 } 179 } 180 } 181 182 if (!isStatic) { 183 ret ~= prefix; 184 ret ~= "alias fn_"; 185 ret ~= func.name; 186 ret ~= " = extern(System) "; 187 ret ~= func.returnType; 188 ret ~= " function("; 189 ret ~= argsSignature; 190 ret ~= ") @system @nogc nothrow;\n"; 191 } 192 193 if (i == 0) { 194 ret ~= "\n"; 195 196 ret ~= prefix; 197 ret ~= "/++\n"; 198 199 ret.genDDOC(family, prefixComment); 200 201 ret ~= prefix; 202 ret ~= " +/\n"; 203 } else { 204 ret ~= prefix; 205 ret ~= "/// Ditto\n"; 206 } 207 208 OGLIntroducedIn introducedIn = family.introducedIn; 209 if (func.introducedIn != OGLIntroducedIn.Unknown) 210 introducedIn = func.introducedIn; 211 212 if (introducedIn != OGLIntroducedIn.Unknown) { 213 ret ~= prefix; 214 ret ~= "@OpenGL_Version(OGLIntroducedIn."; 215 ret ~= introducedIn.text; 216 ret ~= ")\n"; 217 } else { 218 ret ~= prefix; 219 ret ~= "@OpenGL_Version(OGLIntroducedIn.Unknown)\n"; 220 } 221 222 if (func.introducedInExtension !is null) { 223 ret ~= prefix; 224 ret ~= "@OpenGL_Extension(\""; 225 ret ~= func.introducedInExtension; 226 ret ~= "\")\n"; 227 } 228 229 ret ~= prefix; 230 231 if (!isStatic) { 232 ret ~= "fn_"; 233 ret ~= func.name; 234 ret ~= " "; 235 ret ~= func.name; 236 ret ~= ";\n"; 237 } else { 238 ret ~= func.returnType; 239 ret ~= " "; 240 ret ~= func.name; 241 ret ~= "("; 242 ret ~= argsSignature; 243 ret ~= ") @system @nogc nothrow;\n"; 244 } 245 } 246 } 247 248 if (isStatic) { 249 ret ~= prefixContainer; 250 ret ~= "}\n"; 251 } 252 253 if (containerStruct !is null) { 254 ret ~= "}\n"; 255 } 256 257 import std.file : write; 258 write(filename, ret); 259 } 260 261 void genDDOC(T)(ref T ret, OGLFunctionFamily family, string prefix) { 262 string prefix2 = prefix ~ " "; 263 string prefix3 = prefix ~ " "; 264 265 ret ~= prefix; 266 ret ~= family.familyOfFunction; 267 ret ~= ": "; 268 ret ~= family.fromFilename; 269 ret ~= "\n"; 270 ret ~= prefix; 271 ret ~= "\n"; 272 273 ret ~= prefix; 274 ret.genDDOC(family.familyOfFunction, family.docs_description, prefix, prefix); 275 if (ret[$ - 1] != '\n') 276 ret ~= "\n"; 277 ret ~= prefix; 278 ret ~= "\n"; 279 280 if (family.docs_notes.value_children.length > 0) { 281 ret ~= prefix; 282 ret.genDDOC(family.familyOfFunction, family.docs_notes, prefix, prefix); 283 if (ret[$ - 1] != '\n') 284 ret ~= "\n"; 285 ret ~= prefix; 286 ret ~= "\n"; 287 } 288 289 if (ret.length >= prefix.length * 2 + 2 290 && ret[$ - prefix.length - 1 .. $ - 1] == prefix 291 && ret[$ - prefix.length * 2 - 2 .. $ - prefix.length - 2] == prefix) 292 // 2 empty lines here already, no need for another one 293 ret.length--; 294 else 295 ret ~= prefix; 296 ret ~= "Params:\n"; 297 298 size_t longestParam = 0; 299 foreach(ref param; family.docs_parameters) { 300 size_t length = 1; 301 foreach(i, name; param.appliesToNames) { 302 if (i > 0) { 303 length += ", ".length; 304 } 305 306 if (name == "ref") 307 length += "ref_".length; 308 else 309 length += name.length; 310 } 311 if (length > longestParam) 312 longestParam = length; 313 } 314 315 foreach(ref param; family.docs_parameters) { 316 ret ~= prefix2; 317 318 size_t length = 0; 319 foreach(i, name; param.appliesToNames) { 320 if (i > 0) { 321 ret ~= ", "; 322 length += ", ".length; 323 } 324 325 if (name == "ref") { 326 ret ~= "ref_"; 327 length += "ref_".length; 328 } else { 329 ret ~= name; 330 length += name.length; 331 } 332 } 333 334 for (size_t i = 0; i < longestParam - length; i++) 335 ret ~= ' '; 336 ret ~= "= "; 337 ret.genDDOC(family.familyOfFunction, param.documentation, "", prefix3); 338 if (ret[$ - 1] != '\n') 339 ret ~= "\n"; 340 } 341 342 ret ~= prefix; 343 ret ~= "\n"; 344 345 ret ~= prefix; 346 ret ~= "Copyright:\n"; 347 ret ~= prefix2; 348 ret.genDDOC(family.familyOfFunction, family.docs_copyright, prefix2, prefix2); 349 if (ret[$ - 1] != '\n') 350 ret ~= "\n"; 351 ret ~= prefix; 352 ret ~= "\n"; 353 354 ret ~= prefix; 355 ret ~= "See_Also:\n"; 356 ret ~= prefix2; 357 ret.genDDOC(family.familyOfFunction, family.docs_seealso, prefix2, prefix2); 358 if (ret[$ - 1] != '\n') 359 ret ~= "\n"; 360 } 361 362 void genDDOC(T)(ref T ret, string functionFamily, ref OGLDocumentation ctx, string linetabs, string linetabsNext) { 363 bool firstText=true; 364 genDDOC(ret, functionFamily, ctx, linetabs, linetabsNext, firstText); 365 } 366 367 void genDDOC(T)(ref T ret, string functionFamily, ref OGLDocumentation ctx, string linetabs, string linetabsNext, ref bool firstText, bool inCode = false) { 368 import std.string : splitLines, strip, stripRight, KeepTerminator; 369 import std.algorithm : canFind; 370 with(ctx) { 371 string suffix; 372 string macroPrefix, htmlTag; 373 bool startCodeBlock = false; 374 375 switch(type) { 376 case OGLDocumentationType.LookupParameter: 377 macroPrefix = "D_INLINECODE"; 378 goto case OGLDocumentationType.Container; 379 case OGLDocumentationType.LookupConstant: 380 macroPrefix = "D_INLINECODE"; 381 goto case OGLDocumentationType.Container; 382 case OGLDocumentationType.LookupFunction: 383 macroPrefix = "D_INLINECODE"; 384 goto case OGLDocumentationType.Container; 385 case OGLDocumentationType.Title: 386 htmlTag = "h3"; 387 goto case OGLDocumentationType.Container; 388 389 case OGLDocumentationType.TableContainer: 390 import std.conv : text; 391 suffix = "table>[cols=" ~ value_numcols.text ~ "]"; 392 goto case OGLDocumentationType.Container; 393 case OGLDocumentationType.TableHeader: 394 suffix = "head"; 395 goto case OGLDocumentationType.Container; 396 case OGLDocumentationType.TableBody: 397 suffix = "body"; 398 goto case OGLDocumentationType.Container; 399 case OGLDocumentationType.TableRow: 400 suffix = "row"; 401 goto case OGLDocumentationType.Container; 402 case OGLDocumentationType.TableEntry: 403 suffix = "entry"; 404 goto case OGLDocumentationType.Container; 405 case OGLDocumentationType.Copyright: 406 ret ~= "©"; 407 return; 408 case OGLDocumentationType.Trademark: 409 ret ~= "™"; 410 return; 411 case OGLDocumentationType.StyleContainer: 412 if (value_string == "bold") { 413 macroPrefix = "B"; 414 } 415 goto case OGLDocumentationType.Container; 416 case OGLDocumentationType.StyleCode: 417 macroPrefix = "D_CODE"; 418 foreach(child; value_children) { 419 // only start blocks for multiline code 420 startCodeBlock = startCodeBlock || child.value_string.canFind('\n'); 421 } 422 if (!startCodeBlock) 423 macroPrefix = "D_INLINECODE"; 424 goto case OGLDocumentationType.Container; 425 case OGLDocumentationType.Footnote: 426 suffix = "\\/footnote"; 427 goto case OGLDocumentationType.Container; 428 case OGLDocumentationType.InlineEquation: 429 suffix = "|<equation"; 430 goto case OGLDocumentationType.Container; 431 432 case OGLDocumentationType.MathMLContainer: 433 suffix = "MathML[_]"; 434 goto case OGLDocumentationType.Container; 435 case OGLDocumentationType.MathML_MI: 436 suffix = "MathML:mi"; 437 goto case OGLDocumentationType.Container; 438 case OGLDocumentationType.MathML_mn: 439 suffix = "MathML:mn"; 440 goto case OGLDocumentationType.Container; 441 case OGLDocumentationType.MathML_mrow: 442 suffix = "MathML:mrow"; 443 goto case OGLDocumentationType.Container; 444 case OGLDocumentationType.MathML_msup: 445 suffix = "MathML:msup"; 446 goto case OGLDocumentationType.Container; 447 case OGLDocumentationType.MathML_mo: 448 suffix = "MathML:mo"; 449 goto case OGLDocumentationType.Container; 450 case OGLDocumentationType.MathML_msub: 451 suffix = "MathML:msub"; 452 goto case OGLDocumentationType.Container; 453 case OGLDocumentationType.MathML_mfrac: 454 suffix = "MathML:mfrac"; 455 goto case OGLDocumentationType.Container; 456 case OGLDocumentationType.MathML_mtable: 457 suffix = "MathML:mtable[" ~ value_string ~ "]"; 458 goto case OGLDocumentationType.Container; 459 case OGLDocumentationType.MathML_mtr: 460 suffix = "MathML:mtr"; 461 goto case OGLDocumentationType.Container; 462 case OGLDocumentationType.MathML_mtd: 463 suffix = "MathML:mtd[" ~ value_string ~ "]"; 464 goto case OGLDocumentationType.Container; 465 case OGLDocumentationType.MathML_mspace: 466 suffix = "MathML:mspace[ " ~ value_string ~ " ]"; 467 goto case OGLDocumentationType.Container; 468 case OGLDocumentationType.MathML_mtext: 469 suffix = "MathML:mtext[ " ~ value_string ~ " ]"; 470 goto case OGLDocumentationType.Container; 471 case OGLDocumentationType.MathML_apply: 472 suffix = "MathML:apply[ " ~ value_string ~ " ]"; 473 goto case OGLDocumentationType.Container; 474 case OGLDocumentationType.MathML_mover: 475 suffix = "MathML:mover"; 476 goto case OGLDocumentationType.Container; 477 case OGLDocumentationType.MathML_munderover: 478 suffix = "MathML:munderover"; 479 goto case OGLDocumentationType.Container; 480 case OGLDocumentationType.MathML_msqrt: 481 suffix = "MathML:msqrt"; 482 goto case OGLDocumentationType.Container; 483 484 case OGLDocumentationType.IndexList: 485 macroPrefix = "OL"; 486 goto case OGLDocumentationType.Container; 487 case OGLDocumentationType.IndexItem: 488 macroPrefix = "LI"; 489 goto case OGLDocumentationType.Container; 490 491 case OGLDocumentationType.Link: 492 macroPrefix = "LINK2 " ~ value_string ~ ","; 493 goto case OGLDocumentationType.Container; 494 case OGLDocumentationType.StyleSuperScript: 495 htmlTag = "sup"; 496 goto case OGLDocumentationType.Container; 497 case OGLDocumentationType.StyleSubScript: 498 htmlTag = "sub"; 499 goto case OGLDocumentationType.Container; 500 501 case OGLDocumentationType.Paragraph: 502 goto case OGLDocumentationType.Container; 503 504 case OGLDocumentationType.Container: 505 if (startCodeBlock && ret.length >= 1 && ret[$ - 1] != '\n') 506 ret ~= "\n" ~ linetabs ~ "\n" ~ linetabs; 507 508 size_t preMacroPos = -1; 509 if (startCodeBlock) { 510 ret ~= "---\n" ~ linetabs; 511 } else if (macroPrefix !is null) { 512 preMacroPos = ret.length; 513 ret ~= "$("; 514 ret ~= macroPrefix; 515 ret ~= " "; 516 } else if (htmlTag !is null) { 517 ret ~= "<"; 518 ret ~= htmlTag; 519 ret ~= ">"; 520 } 521 522 size_t codePos = ret.length; 523 foreach(i, child; value_children) 524 ret.genDDOC(functionFamily, child, linetabs, linetabsNext, firstText, startCodeBlock); 525 if (codePos < ret.length && ret[codePos] == ' ' && preMacroPos != -1) { 526 // space inside macro, remove space and place it before macro 527 for (size_t i = codePos - 1; i >= preMacroPos; i--) 528 ret[i] = ret[i - 1]; 529 ret[preMacroPos] = ' '; 530 } 531 532 if (startCodeBlock) { 533 if (ret.length >= linetabs.length && ret[$ - linetabs.length .. $] == linetabs) 534 ret ~= "---\n" ~ linetabs; 535 else { 536 if (ret.length && ret[$ - 1] != '\n') 537 ret ~= '\n' ~ linetabs; 538 ret ~= "---\n" ~ linetabs; 539 } 540 } else if (macroPrefix !is null) { 541 if (macroPrefix == "D_INLINECODE" && ret.length && ret[$ - 1] == '\n') 542 ret[$ - 1] = ')'; 543 else 544 ret ~= ")"; 545 } else if (htmlTag !is null) { 546 ret ~= "</"; 547 ret ~= htmlTag; 548 ret ~= ">"; 549 } 550 return; 551 552 case OGLDocumentationType.Text: 553 if (inCode) { 554 foreach(line; value_string.splitLines(KeepTerminator.yes)) { 555 if (line.length >= 4 && line[0 .. 4] == " ") 556 ret ~= line[4 .. $]; 557 else 558 ret ~= line; 559 firstText = false; 560 if (ret.length > 0 && ret[$ - 1] == '\n') 561 ret ~= linetabs; 562 } 563 } else { 564 size_t i; 565 string lines = value_string; 566 567 string linesOld = lines; 568 lines = lines 569 .replace("NULL", "null") 570 .replace("== null", "is null"); 571 572 foreach(line; lines.splitLines) { 573 string lineStripped = line.strip; 574 if (lineStripped.length > 0) { 575 if (!firstText && !(line[0] == '.' || line[0] == ',' || line[0] == ';')) { 576 ret ~= " "; 577 } 578 579 ret ~= lineStripped; 580 i++; 581 firstText = false; 582 583 if (linesOld != lines) { 584 ret ~= "\n" ~ linetabs; 585 } 586 } 587 } 588 } 589 return; 590 case OGLDocumentationType.MathML_mfenced: 591 foreach(i, child; value_children) 592 ret.genDDOC(functionFamily, child, linetabsNext, linetabsNext, firstText); 593 return; 594 case OGLDocumentationType.MathML_floor: 595 ret ~= "<mml:floor/>"; 596 return; 597 case OGLDocumentationType.MathML_infinity: 598 ret ~= "∞"; 599 return; 600 601 case OGLDocumentationType.Unknown: 602 default: 603 // DO NOTHING!!!!! 604 return; 605 606 } 607 } 608 }